//
//  tdl/utils/string.h
//  Created by tommywang on 13-4-22.
//

#ifndef tdl_utils_string_h
#define tdl_utils_string_h

extern "C" {
#include <time.h>
#include <stdlib.h>
} // extern "C"
#include <map>
#include <set>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <iostream>
using namespace std;
#include "utils/time.h"
using namespace tdl;

namespace tdl {
///////////////
namespace container_detail {
////////////////////////////
	template <typename Telm>
	inline void insert(std::vector<Telm>& c, const Telm& e)
	{ c.push_back(e); }
	
	template <typename Telm>
	inline void insert(std::set<Telm>& c, const Telm& e)
	{ c.insert(e); }
} // namespace container_detail

// split @str into parts by @pattern, and store into container @c.
// NOTE: @str will be changed here!
template <typename T>
size_t split_string(std::string& str, const std::string& pattern, T& c)
{
	c.clear();
	char *seg, *brkt;
	for(seg = ::strtok_r((char*)str.c_str(), pattern.c_str(), &brkt); seg; seg = ::strtok_r(NULL, pattern.c_str(), &brkt))
		container_detail::insert(c, std::string(seg));
	
	return c.size();
}

///////////////////////
// string -> xxx
inline uint32_t s2u (const std::string& s, int base = 10) { return ::strtoul(s.c_str(), NULL, base); }
inline  int32_t s2i (const std::string& s, int base = 10) { return ::strtol(s.c_str(), NULL, base); }
inline uint64_t s2ul(const std::string& s, int base = 10) { return ::strtoull(s.c_str(), NULL, base); }
inline  int64_t s2l (const std::string& s, int base = 10) { return ::strtoll(s.c_str(), NULL, base); }

inline uint32_t s2u (const char* p, int base = 10) { return ::strtoul(p, NULL, base); }
inline  int32_t s2i (const char* p, int base = 10) { return ::strtol(p, NULL, base); }
inline uint64_t s2ul(const char* p, int base = 10) { return ::strtoull(p, NULL, base); }
inline  int64_t s2l (const char* p, int base = 10) { return ::strtoll(p, NULL, base); }

inline float s2f(const std::string& s) { return ::strtof(s.c_str(), NULL); }
inline float s2f(const char* p) { return ::strtof(p, NULL); }

uint32_t s2ipv4(const char* s, size_t len)
{
	uint32_t ip = 0;

	char seg[4];
	int j = 0, ndot = 3;
	for(size_t i = 0; i < len; ++i)
	{
		const char c = s[i];
		if(c != '.')
			seg[j++] = c;
		else
		{
			seg[j] = '\0';
			ip |= (s2u(seg) << (8 * ndot));
			j = 0;
			--ndot;
		}
	}
	seg[j] = '\0';
	ip |= s2u(seg);
	return (ndot == 0) ? ip : 0;
}

uint32_t s2ipv4(const std::string& s)
{
	return s2ipv4(s.c_str(), s.length());
}

namespace tostring_detail {
///////////////////////////
const static char* byte2ascii[] = {
		"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 
		"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 
		"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", 
		"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", 
		"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", 
		"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", 
		"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", 
		"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", 
		"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", 
		"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", 
		"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", 
		"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", 
		"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", 
		"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", 
		"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", 
		"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" 
	};
////////////////
}	// namespace tostring_detail

void bin2vs(const char* p, size_t l, std::vector<std::string>& vs)
{
	string s;
	uint8_t* pbyte = (uint8_t*)p;

	for(size_t i = 0; i < l; ++i)
	{
		uint8_t b = pbyte[i];
		s += tostring_detail::byte2ascii[b];

		if((i + 1) % 4 == 0)
		{
			vs.push_back(s);
			s = "";
		}
	}

	vs.push_back(s);
}

std::string bin2s(const char* p, size_t plen, bool tab, bool nline)
{
	std::string s = "";
	std::vector<std::string> vs;
	bin2vs(p, plen, vs);

	for(size_t i = 0; i < vs.size(); ++i)
	{
		if(nline && i > 0 && i % 4 == 0)	s += "\n";
		if(tab)		s += "0x";
		s += vs[i];
		if(tab)		s += "\t";
	}

	return s;
}

/////////////////
// xxx -> string
namespace tostring_detail {
///////////////////////////
	template <typename T>
	std::string dec2s(T i, const char* fmt)
	{
		char buf[20] = { 0 };
		::sprintf(buf, fmt, i);
		return std::string(buf);
	}

	template <typename T>
	std::string hex2s(T i, const char* fmt, bool pfx0)
	{
		char buf[20] = { 0 };

		int l = ::sprintf(buf, fmt, i);

		if(pfx0)
		{
			size_t n0 = 8 - l % 8;
			if(n0 > 0)
				return std::string(n0, '0') + std::string(buf);
		}
		return std::string(buf);
	}

//////////////////////////
}	// namespace tostring_detail

inline std::string u2s(uint32_t i, int base = 10, bool pfx0 = false)
{
	switch(base)
	{
	case 10: return tostring_detail::dec2s(i, "%u");
	case 16: return tostring_detail::hex2s(i, "%x", pfx0);
	default: return "";
	}
}

inline std::string i2s(int32_t i, int base = 10, bool pfx0 = false)
{
	switch(base)
	{
	case 10: return tostring_detail::dec2s(i, "%d");
	case 16: return tostring_detail::hex2s(i, "%x", pfx0);
	default: return "";
	}
}

inline std::string ul2s(uint64_t i, int base = 10, bool pfx0 = false)
{
	switch(base)
	{
	case 10: return tostring_detail::dec2s(i, "%ld");
	case 16: return tostring_detail::hex2s(i, "%lx", pfx0);
	default: return "";
	}
}

inline std::string l2s(int64_t i, int base = 10, bool pfx0 = false)
{
	switch(base)
	{
	case 10: return tostring_detail::dec2s(i, "%ld");
	case 16: return tostring_detail::hex2s(i, "%lx", pfx0);
	default: return "";
	}
}

inline std::string ipv42s(uint32_t ip)
{
	int n = 3;
	string s = u2s((ip & 0xFF000000) >> (8 * n--));
	s += "." + u2s((ip & 0x00FF0000) >> (8 * n--));
	s += "." + u2s((ip & 0x0000FF00) >> (8 * n--));
	s += "." + u2s((ip & 0x000000FF));
	return s;
}

void bin2vs(const char* p, size_t l, std::vector<std::string>& vs);
std::string bin2s(const char* p, size_t plen, bool tab = true, bool nline = true);

// T: time_t / msec_t / struct timeval
template <typename T>
inline std::string t2s_date(const T& t)
{
	return t2s_date<struct tm>(tdl::get_tm(t));
}
template <>
inline std::string t2s_date<struct tm>(const struct tm& t)
{
	std::ostringstream oss;
	oss << (t.tm_year + 1900) << '-'
		<< std::setfill('0') << std::setw(2) << (t.tm_mon + 1) << '-'
		<< std::setfill('0') << std::setw(2) << t.tm_mday;
	return oss.str();
}

// T: time_t / msec_t / struct timeval
template <typename T>
inline std::string t2s_time(const T& t, const msec_t* pms = NULL)
{
	return t2s_time<struct tm>(tdl::get_tm(t), pms);
}
template <>
inline std::string t2s_time<struct tm>(const struct tm& t, const msec_t* pms)
{
	std::ostringstream oss;

	oss << std::setfill('0') << std::setw(2) << t.tm_hour << ':'
		<< std::setfill('0') << std::setw(2) << t.tm_min << ':'
		<< std::setfill('0') << std::setw(2) << t.tm_sec;
	
	if(pms)
		oss << '.' << std::setfill('0') << std::setw(3) << *pms;

	return oss.str();
}
template <>
inline std::string t2s_time<msec_t>(const msec_t& t, const msec_t*)
{
	struct tm time;
	msec_t ms = tdl::get_tm(t, time);
	return t2s_time<struct tm>(time, &ms);
}

// T: time_t / msec_t / struct timeval
template <typename T>
inline std::string t2s_datetime(const T& t, const msec_t* pms = NULL)
{
	return t2s_date<T>(t) + ' ' + t2s_time<T>(t, pms);
}

inline struct tm s2t_date(const std::string& s)
{
	struct tm tms = {0};
	std::string tmps(s.data());
	std::vector<std::string> items;

	// 2013-03-15
	split_string(tmps, "--", items);

	tms.tm_year = s2u(items[0]);
	tms.tm_mon = s2u(items[1]);
	tms.tm_mday = s2u(items[2]);
	return tms;
}

inline struct tm s2t_datetime(const std::string& s)
{
	struct tm tms = {0};
	std::string tmps(s.data());
	std::vector<std::string> items;

	// 2013-03-15 22:19:32
	split_string(tmps, "-- ::", items);

	tms.tm_year = s2u(items[0]) - 1900;
	tms.tm_mon = s2u(items[1]) - 1;
	tms.tm_mday = s2u(items[2]);
	tms.tm_hour = s2u(items[3]);
	tms.tm_min = s2u(items[4]);
	tms.tm_sec = s2u(items[5]);
	return tms;
}

////////////////////
// fixme
/*std::string trim_left(const std::string &s,const std::string& filt = " ");
std::string trim_right(const std::string &s,const std::string& filt = " ");
inline std::string trim(const std::string &s,const std::string& filt = " ")
{
	return trim_right(trim_left(s, filt), filt);
}
*/

void parse_cgi_params(const std::string& s, std::map<std::string, std::string>& params)
{
	std::string req_str(s.data());

	std::vector<std::string> items;
	split_string(req_str, "&", items);

	params.clear();
	for(std::vector<std::string>::iterator it = items.begin(); it != items.end(); ++it)
	{
		std::vector<std::string> kvs;
		std::string tmps((*it).data());
		split_string(tmps, "=", kvs);

		std::string value = (kvs.size() == 2) ? kvs[1] : "";
		params.insert(make_pair(kvs[0], value));
	}
}

/////////////
}	// namespace tdl

#endif

